<?php

namespace app\service;

use share\service\auth\AppleAuthService;
use share\service\auth\WechatAuthService;
use app\exception\ErrnoPlus;
use app\service\cache\DeviceGroupTableCacheService;
use share\exception\ApiException;
use share\exception\Errno;
use share\service\IdMakerService;
use share\service\SendCodeService;
use share\service\SmsLimitService;
use share\service\SoftAccessService;
use share\service\UserAuthService;
use share\service\UserBaseService;
use share\service\UserBindService;
use share\service\UserDetailService;
use share\service\UserPassService;
use share\utils\Helper;
use think\Log;

class AccountService
{

    /**
     * 只为发送短信/邮件时确定位置
     */
    public static function getLocation($param)
    {
        if (isset($param['language'])) {
            $language = strtolower($param['language']);
            if (strpos($language, 'cn') !== false || strpos($language, 'zh') !== false || strpos($language, 'hk') !== false || strpos($language, 'tw') !== false || strpos($language, 'mo') !== false) {
                return 'cn';
            }
        }
        if (isset($param['isoCode'])) {
            $isoCode = strtolower($param['isoCode']);
            if (strpos($isoCode, 'cn') !== false || strpos($isoCode, 'zh') !== false || strpos($isoCode, 'hk') !== false || strpos($isoCode, 'tw') !== false || strpos($isoCode, 'mo') !== false) {
                return 'cn';
            }
        }
        return 'en';
    }


    public static function getByAccount($account, $filter = [])
    {
        $isPhone = strstr($account, '@') ? false : true;
        return $isPhone ? UserBaseService::getByPhone($account, $filter) : UserBaseService::getByEmail($account, $filter);
    }

    public static function sendTransferCodeByEmail($email,$param,$vendor,$packageName){
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_TRANSFER_DEVICE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function sendTransferCodeByPhone($phone,$param, $ip, $vendor,$packageName){
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_TRANSFER_DEVICE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_TRANSFER_DEVICE);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();

        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function getRegisterCodeByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $user = UserBaseService::getByEmail($email, ["vendor" => $vendor]);
        if (!empty($user)) {
            throw new ApiException("you can not  use this account ", ErrnoPlus::USER_EXISTS);
        }
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function getRegisterCodeByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $filter = ["vendor" => $vendor];
        // if (isset($param['phoneCode'])) {
        //     $filter['phone_code'] = $param['phoneCode'];
        // }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $user = UserBaseService::getByPhone($phone, $filter);

        if (!empty($user)) {
            throw new ApiException("you can not  use this account ", ErrnoPlus::USER_EXISTS);
        }

        if (isset($param['phoneCode'])) {
            $countryCodeInfo = CountryCodeService::getBase($param['location']);
            if (empty($countryCodeInfo)) {
                throw new ApiException("this location is not  support send sms", ErrnoPlus::NOT_SUPPORT_SMS);
            }
            if ($countryCodeInfo['phone_code'] !== $param['phoneCode']) {
                throw new ApiException("phone Code is error", ErrnoPlus::COMMON_ERROR);
            }
        }

        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_REGISTER);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();

        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 通过邮箱注册
     */
    public static function registerByEmail($email, $param)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (empty($code)) { // code 已过期
            throw new ApiException("this verification code is out time", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code !== $param["verifyCode"]) {
            throw new ApiException("this verification code error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();
        $user = UserBaseService::getByEmail($email, ["vendor" => $param['vendor']]);
        if (!empty($user)  && $user['status'] === UserBaseService::USER_STATE_OF_NORMAL) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }
        $password = UserBaseService::decodePassword(urldecode($param["password"]), config("KEY_BY_3DES"), true);
        if (isset($param['unencodePass']) && $param['unencodePass']) { //密码明文
            $password = $param["password"];
        }
        $tn = time();
        if (!empty($user)) { // 账号已存在
            UserBaseService::update($user['id'], [
                'status' => UserBaseService::USER_STATE_OF_NORMAL,
                'token' => Helper::getUnique(),
            ]);
            UserDetailService::update($user['id'], [
                'nick_name' => $email,
                'modifytime' => $tn,
            ]);
            UserPassService::upsertUserPass($user['id'], $password);
        } else {
            $uid = IdMakerService::make(IdMakerService::NAME_OF_USER_BASE);
            $info = [
                'id' => $uid,
                'email' => $email,
                'vendor' => $param['vendor'],
                'user_id' => Helper::getUnique(),
                'token' => Helper::getUnique(),
            ];
            $detail = [
                'id' => $uid,
                'nick_name' => $email,
                'location' => $param['location'],
                'firsttime' => $tn,
            ];

            $ret = UserBaseService::addFullUser($info,$detail);
            if(!$ret){                
                throw new ApiException("system busy. add user error", Errno::COMMON_SYSTEM_ERROR);
            }

            UserPassService::addPass($uid, $password);
        }
    }

    /**
     * 通过手机号注册
     */
    public static function registerByPhone($phone, $param)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (empty($code)) { // code 已过期
            throw new ApiException("this verification code is out time", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code !== $param["verifyCode"]) {
            throw new ApiException("this verification code error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();
        $phoneCode = isset($param["phoneCode"]) ? $param["phoneCode"] : "86";
        $user = UserBaseService::getByPhone($phone, ["vendor" => $param['vendor']]);
        if (!empty($user)  && $user['status'] === UserBaseService::USER_STATE_OF_NORMAL) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }
        $password = UserBaseService::decodePassword(urldecode($param["password"]), config("KEY_BY_3DES"), true);
        if (isset($param['unencodePass']) && $param['unencodePass']) { //密码明文
            $password = $param["password"];
        }
        $tn = time();
        if (!empty($user)) { // 账号已存在
            UserBaseService::update($user['id'], [
                'status' => UserBaseService::USER_STATE_OF_NORMAL,
                'token' => Helper::getUnique(),
            ]);
            UserDetailService::update($user['id'],[
                'nick_name' => $phone,
                'modifytime' => $tn,
            ]);
            UserPassService::upsertUserPass($user['user_id'], $password);
        } else {
            $uid = IdMakerService::make(IdMakerService::NAME_OF_USER_BASE);
            $info = [
                'id' => $uid,
                'phone' => $phone,                
                'vendor' => $param['vendor'],
                'user_id' => Helper::getUnique(),
                'token' => Helper::getUnique(), 
            ];
            $detail = [
                'id' => $uid,
                'phone_code' => $phoneCode,  
                'nick_name' => $phone,
                'location' => $param['location'],
                'firsttime' => $tn,
            ];
            $ret = UserBaseService::addFullUser($info,$detail);
            if(!$ret){                
                throw new ApiException("system busy. add user error", Errno::COMMON_SYSTEM_ERROR);
            }
            UserPassService::addPass($uid, $password);
        }
    }

    /**
     * 更换邮件账户 发送验证码
     */
    public static function sendChangeAccountCodeByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_USER_CHANGE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $user = UserBaseService::getByEmail($email, ["vendor" => $vendor]);
        if (!empty($user)) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);

        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
                'oldAccount' => $param['userAccount'],
                'newAccount' => $param['changeAccount'],
                'appName' => $param['appName'],
            ]
        ];
        $sendCodeService->setParam($sendParam);

        $sendCodeService->addToQueue();
    }

    /**
     * 更换手机账户 发送验证码
     */
    public static function sendChangeAccountCodeByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_USER_CHANGE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $user = UserBaseService::getByPhone($phone, ["vendor" => $param['vendor']]);
        if (!empty($user)) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }
        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) {
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }
        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_USER_CHANGE);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();

        $smsLimit->addRecord();

        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function changeAccountByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_USER_CHANGE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (empty($code)) { // code 已过期
            throw new ApiException("this verification code is out time", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code !== $param["verifyCode"]) {
            throw new ApiException("this verification code error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();
        $user = UserBaseService::getByEmail($email, ["vendor" => $vendor]);
        if (!empty($user)) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }
        $programme = $param["vendor"] . "_" . $param['language'] . "_received_account";
        $programme = SendCodeBridgeService::makeProgrammeFormat($programme);
        $originUser = UserBaseService::getByEmail($param['userAccount'], ["vendor" => $vendor]);
        $tn = time();
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);

        UserBaseService::update($originUser['id'], [
            'email' => $email,
            'status' => UserBaseService::USER_STATE_OF_NORMAL,

        ]);
        UserDetailService::update($originUser['id'],[
            'nick_name' => $email,
            'modifytime' => $tn,
        ]);
        UserPassService::upsertUserPass($originUser['id'], $code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
                'appName' => isset($param['appName']) ? $param['appName'] :  $packageName,
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function changeAccountByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_USER_CHANGE,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (empty($code)) { // code 已过期
            throw new ApiException("this verification code is out time", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code !== $param["verifyCode"]) {
            throw new ApiException("this verification code error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();
        $user = UserBaseService::getByPhone($phone, ["vendor" => $vendor]);
        if (!empty($user)) {
            throw new ApiException("this user already exists", ErrnoPlus::USER_EXISTS);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }
        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_USER_CHANGE);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();

        $programme = $param["vendor"] . "_sms_" . $param['language'] . "_account_received";
        $programme = SendCodeBridgeService::makeProgrammeFormat($programme);
        $originUser = UserBaseService::getByPhone($param['userAccount'], ["vendor" => $vendor]);
        $tn = time();
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        UserBaseService::update($originUser['id'], [
            'phone' => $phone,
            'status' => UserBaseService::USER_STATE_OF_NORMAL,
        ]);
        UserDetailService::update($originUser['id'], [
            'nick_name' => $phone,
            // 'password' => $code,
            'modifytime' => $tn,
        ]);
        UserPassService::upsertUserPass($originUser['id'], $code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }


    public static function getResetCodeByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_RESET_PASS,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $user = UserBaseService::getByEmail($email, ["vendor" => $vendor]);
        if (empty($user)) {
            throw new ApiException("the account is not exists", ErrnoPlus::USER_NOT_EXISTS);
        }
        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $vendor = isset($param['sendmailVendor']) ?  $param['sendmailVendor'] : $vendor;
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    public static function getResetCodeByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_RESET_PASS,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $filter = ["vendor" => $vendor];
        $user = UserBaseService::getByPhone($phone, $filter);
        
        if (empty($user)) {
            throw new ApiException("the account is not exists ", ErrnoPlus::USER_NOT_EXISTS);
        }
        $userDetail = UserDetailService::getBase($user['id']);
        if (empty($user)) {
            throw new ApiException("user detail info lost ", ErrnoPlus::USER_NOT_EXISTS);
        }
        if ($userDetail['location'] === "CN") {
            $params['location'] = "CN";
        } else {
            $params['location'] = $userDetail['phone_code'] == 86 ? "CN" : "US";
        }
        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_RESET_PASS);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();
        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'ext' => [
                'phoneCode' => $userDetail['phone_code'],
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 重置密码
     */
    public static function resetPassword($param)
    {
        $password = (isset($param['passwordEncode']) && $param['passwordEncode'])
            ? UserBaseService::decodePassword($param['password'], config("KEY_BY_3DES"), true)
            : $param['password'];
        $account = $param['account'];
        $isPhone = strstr($account, '@') ? false : true;
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_RESET_PASS,
            $isPhone ? SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE : SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $account
        );
        $code = $sendCodeService->getCode();
        if (empty($code)) { //上次发送的记录还在
            throw new ApiException("code is not exist", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code != $param['verifyCode']) {
            throw new ApiException("the code is error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();

        $origin = self::getByAccount($account, ['vendor' => $param['vendor']]);

        if (empty($origin) || $origin['status'] != UserBaseService::USER_STATE_OF_NORMAL) {
            throw new ApiException("this user is not exists or state exception", ErrnoPlus::USER_NOT_EXISTS);
        }
        $tn = time();
        $update = [
            'token' => Helper::getUnique(),           
        ];
        UserBaseService::update($origin['id'], $update);
        UserPassService::upsertUserPass($origin['id'], $password);
    }


    /**
     * 发送登录的验证码
     */
    public static function sendLoginCodeByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_LOGIN,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $param["location"] = $param['isoCode'];
        $param["appName"] = isset($param["packageName"]) ? $param["packageName"] : "";
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
                'appName' => $param["appName"]
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 发送登录的验证码
     */
    public static function sendLoginCodeByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_LOGIN,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $filter = ["vendor" => $vendor];
        $user = UserBaseService::getByPhone($phone, $filter);        
        if (empty($user)) {
            throw new ApiException("the account is not exists ", ErrnoPlus::USER_NOT_EXISTS);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_LOGIN);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();

        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $param["location"] = $param['isoCode'];
        $param["appName"] = isset($param["packageName"]) ? $param["packageName"] : "";
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'appName' => $param["appName"]
            ],
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]

        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 
     */
    public static function sendDevicePasswordByEmail($email, $param, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_DEVICE_PASS,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $param["appName"] = isset($checkInfo["packageName"]) ? $checkInfo["packageName"] : "";
        $code = UserBaseService::decodePassword($param['password'], config("KEY_BY_3DES"), true);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
                'appName' => $param["appName"]
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 
     */
    public static function sendDevicePasswordByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_DEVICE_PASS,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_RESET_PASS);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();
        $language = strstr($param["language"], "zh") ? "zh-CN" : "en-US";
        $param["language"] = $language;
        $param["appName"] = isset($checkInfo["packageName"]) ? $checkInfo["packageName"] : "";
        $code = UserBaseService::decodePassword($param['password'], config("KEY_BY_3DES"), true);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'appName' => $param["appName"]
            ],
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 绑定账号前 发送验证码
     */
    public static function sendCodeBindAccountByEmail($email, $param, $vendor, $packageName = '')
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL,
            $email
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $user = UserBaseService::getByEmail($email, ['vendor' => $vendor]);
        if ($user) { //
            self::checkUserBinding($user, $param);
        }
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
                'email' => $email,
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 绑定账号前 发送验证码
     */
    public static function sendCodeBingAccountByPhone($phone, $param, $ip, $vendor, $packageName)
    {
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $phone
        );
        $code = $sendCodeService->getCode();
        if (!empty($code)) { //上次发送的记录还在
            throw new ApiException("code already send near time. please retry late", ErrnoPlus::VERIFICATION_CODE_EXISTS);
        }
        $user = UserBaseService::getByPhone($phone, ['vendor' => $vendor]);
        if ($user) { //
            self::checkUserBinding($user, $param);
        }

        $smsLimit = SmsLimitService::instance($phone, $ip);
        if (!$smsLimit->check()) { //发送数量过多被限制  
            throw new ApiException("Too many request , please try again later.", ErrnoPlus::SMS_CODE_SEND_LIMIT);
        }

        $softAccessService =  SoftAccessService::instance($ip, SoftAccessService::SOFT_ACCESS_SCENE_REGISTER);
        // 访问限制
        if ($softAccessService->isNeedAuth()) {
            $token = isset($param['accessToken']) ? $param['accessToken'] : '';
            if (empty($token)) {
                throw new ApiException("we need your access token", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
            //访问限制未通过
            if (!$softAccessService->check($token)) {
                throw new ApiException("please auth.", ErrnoPlus::NEED_SOFT_ACCESS_AUTH);
            }
        }
        // 增加访问次数
        $softAccessService->addVisitTimes();
        $smsLimit->addRecord();
        $code = Helper::getRandomStr(6, Helper::RANDOM_STR_TYPE_NUM);
        $sendCodeService->setCode($code);
        $location = self::getLocation($param);
        $sendParam = [
            'vendor' => $vendor,
            'package' => $packageName,
            'language' => $location == 'cn' ? 'zh-cn' : 'en-us',
            'location' => $location,
            'replace' => [
                'code' => $code,
            ],
            'ext' => [
                'phoneCode' => isset($param['phoneCode']) ? $param['phoneCode'] : '',
                'isoCode' => isset($param['isoCode']) ? $param['isoCode'] : '',
            ]
        ];
        $sendCodeService->setParam($sendParam);
        $sendCodeService->addToQueue();
    }

    /**
     * 账号绑定
     */
    public static function bindingAccount($param, $vendor)
    {
        $isEmail = strpos($param['account'], '@') === false ? false : true;
        $sendCodeService = SendCodeService::instance(
            SendCodeService::SEND_CODE_SCENE_REGISTER,
            $isEmail ?  SendCodeService::SEND_CODE_SEND_TYPE_OF_EMAIL : SendCodeService::SEND_CODE_SEND_TYPE_OF_PHONE,
            $param['account']
        );

        $code = $sendCodeService->getCode();
        if (!$code) {
            throw new ApiException("the code is expired", ErrnoPlus::VERIFICATION_CODE_OUTTIME);
        }
        if ($code !== $param['verifyCode']) {
            throw new ApiException("code error", ErrnoPlus::VERIFICATION_CODE_ERROR);
        }
        $sendCodeService->clear();

        $openInfo = UserAuthService::getTmpOpenInfo($param['openId']);
        if (empty($openInfo)) {
            throw new ApiException("open info is timeout or not exist", ErrnoPlus::COMMON_INVALID_INPUT);
        }
        if ($openInfo['vendor'] != $vendor) {
            throw new ApiException("open info error", ErrnoPlus::COMMON_INVALID_INPUT);
        }
        if ($openInfo['open_type'] == UserBindService::OPEN_TYPE_OF_WECHAT &&  $param['loginType'] !== 'wechat') {
            throw new ApiException("loginType info error", ErrnoPlus::COMMON_INVALID_INPUT);
        }
        if ($openInfo['open_type'] == UserBindService::OPEN_TYPE_OF_APPLE &&  $param['loginType'] !== 'apple') {
            throw new ApiException("loginType info error", ErrnoPlus::COMMON_INVALID_INPUT);
        }
        $openId = $openInfo['open_id'];
        unset($openInfo['open_id']);
        $bindUser = UserBindService::getByOpenId($openId, $openInfo);
        if ($bindUser) {
            //已经绑定了其他账号
            throw new ApiException("already bind a other account.", ErrnoPlus::USER_EXISTS);
        }

        $userBase = $isEmail ? UserBaseService::getByEmail($param['account'], ['vendor' => $vendor]) : UserBaseService::getByPhone($param['account'], ['vendor' => $vendor]);
        if ($userBase) { //要绑定的账号已存在
            UserBindService::bind($userBase['id'], $openId, $openInfo['open_type']);
            return UserAuthService::doAuth($userBase);
        } else {
            $uid = IdMakerService::make(IdMakerService::NAME_OF_USER_BASE);
            $baseInfo = [
                "id" => $uid,
                "vendor" => $vendor,
                "user_id" => Helper::getUnique(),
                "token" => Helper::getUnique(),
                "identity_id" => "",
            ];
            $detailInfo = [
                "id" => $uid,
                "nick_name" => $param['account'],
                "location" => isset($param["isoCode"]) ? $param["isoCode"] : '',
                "server_site" => "",
            ];
            if ($isEmail) {
                $baseInfo['email'] = $param['account'];
            } else {
                $baseInfo['phone'] = $param['account'];
                $detailInfo['phone_code'] =  $param["phoneCode"];
            }
            $ret = UserBaseService::addFullUser($baseInfo,$detailInfo);
            if(!$ret){                
                throw new ApiException("system busy. add user error", Errno::COMMON_SYSTEM_ERROR);
            }
            UserBindService::bind($baseInfo['id'], $openId, $openInfo['open_type']);
            return UserAuthService::doAuth($baseInfo);
        }
    }

    /**
     * 通过授权码  绑定第三方账号
     */
    public static function bindByCode($param, $vendor, $userInfo)
    {
        $loginType = $param['loginType'];
        if ($userInfo['vendor'] !== $vendor) {
            throw new ApiException("user vendor error", ErrnoPlus::PARAM_ERROR);
        }
        $openId = '';
        $openType = 0;
        if ($loginType === 'wechat') {
          
            if (!isset($param['appId'])) {
                throw new ApiException("lost appId when bind wechat account", ErrnoPlus::PARAM_ERROR);
            }
            $appInfo = OtherClientTableService::getBase($param['appId']);
            if (empty($appInfo)) {
                throw new ApiException("config of other client is lost", ErrnoPlus::PARAM_ERROR);
            }
            $openUser = WechatAuthService::getUserInfo($appInfo['app_id'], $appInfo['app_secret'], $param['code']);
            if($openUser){
                $openId = $openUser['openid'];            
                $openType = UserBindService::OPEN_TYPE_OF_WECHAT;
            }
 
        } else if ($loginType === 'apple') {
            if (!isset($param["openId"]) || empty($param["openId"])  || empty($param["code"])) {
                throw new ApiException("openId lost when auth by apple", ErrnoPlus::PARAM_ERROR);
            }
            $openUser = AppleAuthService::getUserInfo($param['code']);
            if($openUser){
                $openId = $openUser['openid'];
                $openType = UserBindService::OPEN_TYPE_OF_APPLE;
            }
        }
        if ((!empty($openId)) && $openType) {
            $existOpenUser = UserBindService::getByOpenId($openId,[
                'open_type' => $openType,
                'vendor' => $vendor,
            ]);
            if ($existOpenUser) { 
                if($existOpenUser['uid'] == $userInfo['id']){ //已经绑定过当前账号了
                    return null;
                }
                //  已经绑定了其他账号
                throw new ApiException("already bind other account", ErrnoPlus::OTHER_BINDING);
            }
            else{
                UserBindService::add([
                    'uid' => $userInfo['id'],
                    'open_id' => $openId,
                    'open_type' => $openType,
                    'vendor' => $vendor,
                ]);
            }          
        }
        else{
            throw new ApiException("get third auth info error", ErrnoPlus::COMMON_INVALID_INPUT);
        }
    }

    /**
     * 解绑三方账户
     */
    public static function unbind($param, $userInfo)
    {
        $loginType = $param['loginType'];
        $openType = 0;
        if ($loginType === 'wechat') {
            $openType = UserBindService::OPEN_TYPE_OF_WECHAT;
           
        } else if ($loginType === 'apple') {
            $openType = UserBindService::OPEN_TYPE_OF_APPLE;
        }
        if ($openType > 0) {
            UserBindService::unbind($userInfo['id'],$openType);
        }
    }

    /**
     * 检查绑定关系
     */
    public static function checkUserBinding($user, $param)
    {
        $loginType = $param['loginType'];
        if ($loginType === 'wechat') {
            $userBind = UserBindService::getUserBindInfo($user['id'],UserBindService::OPEN_TYPE_OF_WECHAT);
            if($userBind && $userBind['open_id']) {
                throw new ApiException("account already bind wechat", ErrnoPlus::ACCOUNT_BINDING);
            }
            $userDetail = UserDetailService::getBase($user['id']);
            $location = $userDetail["location"] ? $userDetail["location"] : $userDetail["server_site"];
            if ($location !== $param["isoCode"] || $param["isoCode"] !== "CN") {
                throw new ApiException("Forbidden Binding Wechat except china", ErrnoPlus::FORBIDDEN_BINDING_WX);
            }
        } else if ($loginType === 'apple') {
            $userBind = UserBindService::getUserBindInfo($user['id'],UserBindService::OPEN_TYPE_OF_WECHAT);
            if($userBind && $userBind['open_id']) {
                throw new ApiException("account already bind apple", ErrnoPlus::ACCOUNT_BINDING);
            }
        }
    }

    /**
     * 更新基础信息
     */
    public static function updateBaseInfo($param, $userInfo)
    {
        if (empty($userInfo)) {
            throw new ApiException("context data lost", ErrnoPlus::COMMON_ERROR);
        }
        $map = [
            // 'identityId' => 'identity_id',
            'picture' => 'picture',
            'sex' => 'sex',
            'nickName' => 'nick_name',
            'location' => 'location',
        ];
        $userDetail = UserDetailService::getBase($userInfo['id']);
        $updateDetail = [];
        foreach ($map as $key => $val) {
            if (isset($param[$key]) && $param[$key] != $userDetail[$val]) {
                $updateDetail[$val] = $param[$key];
            }
        }

        if (isset($param['serverSite']) && empty($userDetail['serverSite']) && !empty($param['serverSite'])) {
            $allHost = AliyunService::allHost();
            if (!in_array($param['serverSite'], array_keys($allHost))) {
                throw new ApiException("serverSite " . $param['serverSite'] . " is not allow", ErrnoPlus::COMMON_ERROR);
            }
            $updateDetail['server_site'] = $param['serverSite'];
        }
        if (count($updateDetail) > 0) {
            $tn = time();
            $updateDetail['modifytime'] = $tn;
            UserDetailService::update($userInfo['id'], $updateDetail);
        }

        $updateBase = [];
        if (isset($param['email']) && empty($userInfo['email']) && !empty($param['email'])) {
            if (Helper::checkEmailFormat($param['email'])) {
                $updateBase['email'] = $param['email'];
            }
        }

        if (isset($param['phone']) && empty($userInfo['phone']) && !empty($param['phone'])) {
            if (Helper::checkChinaPhone($param['phone'])) {
                $updateBase['phone'] = $param['phone'];
            }
        }

        if (isset($param['identityId']) && empty($userInfo['identity_id']) && !empty($param['identityId'])) {
            $updateBase['identity_id'] = $param['identityId'];
        }
        UserBaseService::update($userInfo['id'],$updateBase);
        $userBindInfo = UserBindService::getUserFormatBindInfo($userInfo['id']);
        return array_merge($userInfo,$userDetail, $updateBase,$updateDetail,$userBindInfo,['password' => '']);
    }

    /**
     * 修改密码
     */
    public static function changePassword($param, $userInfo)
    {
        if (empty($userInfo)) {
            throw new ApiException("context data lost", ErrnoPlus::COMMON_ERROR);
        }
        if (!UserPassService::comparePass($param['oldPassword'], $userInfo['id'])) {
            throw new ApiException("origin psword error", ErrnoPlus::OLD_PASSWORD_ERROR);
        }
        $token = Helper::getUnique();
        $tn = time();
        $update = [
            "token" => $token,
        ];
        UserBaseService::update($userInfo['id'], $update);
        UserPassService::upsertUserPass($userInfo['id'], $param['newPassword']);
        $update['userId'] = $userInfo['user_id'];
        return $update;
    }

    public static function searchAccount($param)
    {
        $filter = [];
        if (isset($param['account'])) {
            $filter["email|phone"] = ["like", "%" . $param['account'] . "%"];
        }
        if (isset($param['nickName'])) {
            $filter["nick_name"] = ["like", "%" . $param['nickName'] . "%"];
        }
        if (count($filter) < 1) { //条件为空时
            return [];
        }
        $filter['status'] = UserBaseService::USER_STATE_OF_NORMAL;
        $res = UserBaseService::getAllByFilter($filter);
        return $res;
    }

    public static function accountBaseInfo($param)
    {
        $filter = [];
        $account = $param['account'];
        $filter['vendor'] = $param['vendor'];
        $filter['status'] = UserBaseService::USER_STATE_OF_NORMAL;
        $res = self::getByAccount($account, $filter);
        return $res;
    }

    /**
     * 用户注销
     */
    public static function deleteBaseInfo($param, $userInfo)
    {
        $tn = time();
        if (abs($tn - $param['deleteTime']) > 60) {
            throw new ApiException("this request is out time", ErrnoPlus::REQUEST_OUT_TIME);
        }
        if ($param['vendor'] !== $userInfo['vendor']) {
            throw new ApiException("user can not delete by an other vendor", ErrnoPlus::USER_NOT_EXISTS);
        }
        $uid = $userInfo['id'];
        self::deleteAllUserData($uid);
    }

    public static function deleteAllUserData($uid)
    {
        BindingDeviceService::deleteByUser($uid);
        DeviceGroupTableService::deleteByUser($uid);
        UserDeviceShareService::deleteByUser($uid);
        UserDeviceShareService::deleteByShareUser($uid);        
        DeviceGroupTableCacheService::del(["uid" => $uid]);
        UserBaseService::del($uid);
        UserDetailService::del($uid);
    }


    public static function buildCondition($filter)
    {
        $res = [];
        if (isset($filter['userId']) && !empty($filter['userId'])) {
            $res['user_id'] = ['like', '%' . $filter['userId'] . '%'];
        }
        if (isset($filter['identityId']) && !empty($filter['identityId'])) {
            $res['identity_id'] =  ['like', '%' . $filter['identityId'] . '%'];
        }
        if (isset($filter['account']) && !empty($filter['account'])) {
            if (strpos($filter['account'], '@') !==  false) {
                $res['email'] = ['like', '%' . $filter['account'] . '%'];
            } else {
                $res['phone'] = ['like', '%' . $filter['account'] . '%'];
            }
        }
        if (isset($filter['email']) && !empty($filter['email'])) {
            $res['email'] = ['like', '%' . $filter['email'] . '%'];
        }
        if (isset($filter['phone']) && !empty($filter['phone'])) {
            $res['phone'] = ['like', '%' . $filter['phone'] . '%'];
        }
        return $res;
    }

    public static function getListInfo($filter, $pageInfo, $order = null)
    {
        $condition = self::buildCondition($filter);
        return UserBaseService::getPageList($condition, $pageInfo['page'], $pageInfo['size'], $order);
    }

    public static function getListCountInfo($filter)
    {
        $condition = self::buildCondition($filter);
        return UserBaseService::count($condition);
    }

    public static function setUserLocation($userId, $location)
    {
        $userBase = UserBaseService::getByUserId($userId);
        if(empty($userBase)){
            throw new ApiException("user not exist!", ErrnoPlus::COMMON_INVALID_INPUT);
        }
        return UserDetailService::update($userBase['id'], [
            'location' => $location,
        ]);
    }

    /**
     * 今日注册信息 分时统计
     */
    public static function getTodayRegisterInfo($vendor)
    {
        $startTime = strtotime(date("Y-m-d 00:00:00"));
        $endTime = $startTime + 86400;
        return [];
        // return UserDetailService::getModel()->where([
        //     'vendor' => $vendor,
        //     "firsttime" => [
        //         "between", [$startTime, $endTime]
        //     ],
        // ])->group('hour')->field("FROM_UNIXTIME(firsttime,'%H') hour,count(id) count")->select();
    }

    /**
     * 获取用户的设备信息
     */
    public static function getUserDeviceInfo($userId)
    {
        $userInfo = UserBaseService::getByUserId($userId);
        if(empty($userInfo)){
            return [];
        }
        $uid = $userInfo['id'];
        $res = [];
        $shareInfo = UserDeviceShareService::getAllByShareUser($uid);

        $bindingInfo = UserDeviceBindingService::getAllByUser($uid);
        $shareIotIds = array_column($shareInfo, 'iot_id');
        $bindingIotIds = array_column($bindingInfo, 'iot_id');
        $iotIds = Helper::arraySmartUnique(array_merge($shareIotIds, $bindingIotIds));
        $deviceInfos = DeviceInfoTableService::getAllByIotIds($iotIds);
        foreach ($deviceInfos as $device) {
            $tmp = [
                'userId' => $userInfo['user_id'],
                'identityId' => $userInfo['identity_id'],
                'phone' => $userInfo['phone'],
                'email' => $userInfo['email'],
                'deviceName' => $device['device_name'],
                'iotId' => $device['iot_id'],
                'owned' => in_array($device['iot_id'], $bindingIotIds) ? 1 :  0,
            ];
            $res[] = $tmp;
        }
        return $res;
    }
}
